"""平板端数据接收模型定义文件

本文件定义了从平板端接收的脑波设备数据格式，完全替代NeuroPy3设备的数据输出。
平板端将作为脑波设备的代理，通过HTTP接口发送与NeuroPy3相同格式的数据。

数据字段完全对应NeuroPy3的输出：
- 原始EEG数据 (rawValue)
- 8个频段功率数据 (delta, theta, lowAlpha, highAlpha, lowBeta, highBeta, lowGamma, midGamma)
- NeuroSky算法输出 (attention, meditation)
- 信号质量指标 (poorSignal, blinkStrength)

内存优化策略：
- 使用环形缓冲区限制内存使用
- 自动清理过期数据
- 数据压缩和批量处理
- 内存使用监控和告警

使用方法：
- TabletBrainWaveData: 平板端发送的完整脑波数据包
- TabletDataBuffer: 内存优化的数据缓冲区
- TabletDataValidator: 数据验证工具
- TabletDataConverter: 转换为内部BrainWaveData格式

原理：
使用Pydantic进行数据验证，确保平板端数据与NeuroPy3设备数据格式一致。
采用环形缓冲区和定时清理机制，在不使用Redis的情况下支持40个平板端的高频数据传输。
"""

from __future__ import annotations

from pydantic import BaseModel, Field, validator
from typing import Optional, Dict, List, Any
from datetime import datetime, timedelta
from enum import Enum
from collections import deque
import threading
import time
import gc
import os
import logging

# 配置日志
logger = logging.getLogger(__name__)


class RawValuePoint(BaseModel):
    """原始EEG数据点模型，包含值和时间戳"""
    value: int = Field(..., description="EEG原始数据值")
    timestamp: int = Field(..., description="数据点时间戳（毫秒）")
    
    @validator('value')
    def validate_value_range(cls, v):
        """验证数据值范围"""
        if not (-32768 <= v <= 32767):
            raise ValueError('EEG数据值必须在-32768到32767范围内')
        return v


class TabletBrainWaveData(BaseModel):
    """
    平板端脑波数据模型 - 完全对应用户提供的数据格式
    
    这个模型包含了平板端发送的所有数据字段，
    使用驼峰命名法，与前端JavaScript格式保持一致。
    """
    
    # 基本信息 - 统一使用下划线命名
    session_id: str = Field(..., min_length=1, description="会话ID")
    room_id: str = Field(..., min_length=1, description="房间ID") 
    timestamp: int = Field(..., description="数据时间戳（毫秒）")
    
    # 原始EEG数据 - 包含时间戳的数据点数组
    raw_value: List[RawValuePoint] = Field(..., description="原始EEG数据点数组，每个点包含值和时间戳")
    
    # 8个频段功率数据 - 统一使用下划线命名
    delta: int = Field(..., ge=0, description="Delta波段功率 (0.5-4Hz)")
    theta: int = Field(..., ge=0, description="Theta波段功率 (4-8Hz)")
    low_alpha: int = Field(..., ge=0, description="低Alpha波段功率 (8-10Hz)")
    high_alpha: int = Field(..., ge=0, description="高Alpha波段功率 (10-13Hz)")
    low_beta: int = Field(..., ge=0, description="低Beta波段功率 (13-17Hz)")
    high_beta: int = Field(..., ge=0, description="高Beta波段功率 (17-30Hz)")
    low_gamma: int = Field(..., ge=0, description="低Gamma波段功率 (30-40Hz)")
    mid_gamma: int = Field(..., ge=0, description="中Gamma波段功率 (40-100Hz)")
    
    # NeuroSky算法输出
    attention: int = Field(..., ge=0, le=100, description="注意力指数 (0-100)")
    meditation: int = Field(..., ge=0, le=100, description="冥想指数 (0-100)")
    
    # 信号质量指标 - 统一使用下划线命名
    poor_signal: int = Field(..., ge=0, le=200, description="信号质量 (0=好信号, 200=无信号)")
    blink_strength: int = Field(..., ge=0, le=255, description="眨眼强度 (0-255)")

    @validator('raw_value')
    def validate_raw_value(cls, v):
        """验证原始EEG数据数组"""
        if not v:
            raise ValueError('rawValue不能为空')
        
        # 验证时间戳的连续性
        timestamps = [point.timestamp for point in v]
        if len(set(timestamps)) != len(timestamps):
            raise ValueError('rawValue中不能有重复的时间戳')
        
        # 验证时间戳是递增的
        if timestamps != sorted(timestamps):
            raise ValueError('rawValue中的时间戳必须按时间顺序排列')
        
        return v

    @validator('timestamp')
    def validate_timestamp(cls, v):
        """验证时间戳合理性"""
        current_time = int(datetime.now().timestamp() * 1000)
        # 允许时间戳在当前时间前后1小时内（支持长时间测试数据和时区差异）
        if abs(v - current_time) > 60 * 60 * 1000:
            raise ValueError('时间戳超出合理范围')
        return v

    @validator('delta', 'theta', 'low_alpha', 'high_alpha', 'low_beta', 'high_beta', 'low_gamma', 'mid_gamma')
    def validate_frequency_power(cls, v):
        """验证频段功率值合理性"""
        # NeuroPy3的频段功率通常在0-16777215范围内（24位值）
        if v > 16777215:
            raise ValueError('频段功率值超出合理范围')
        return v

    def to_compact_dict(self) -> Dict[str, Any]:
        """转换为紧凑的字典格式，减少内存占用"""
        return {
            's': self.session_id,  # 使用session_id
            'room': self.room_id,  # 使用room_id
            't': self.timestamp,
            'r': [{'v': p.value, 't': p.timestamp} for p in self.raw_value],  # 保持时间戳信息
            'f': [self.delta, self.theta, self.low_alpha, self.high_alpha,
                  self.low_beta, self.high_beta, self.low_gamma, self.mid_gamma],
            'a': self.attention,
            'm': self.meditation,
            'p': self.poor_signal,  # 使用poor_signal
            'b': self.blink_strength  # 使用blink_strength
        }
    
    def to_internal_format(self) -> Dict[str, Any]:
        """转换为内部处理格式，保持与现有代码兼容"""
        return {
            'session_id': self.id,
            'room_id': self.roomId,
            'timestamp': self.timestamp,
            'raw_value': [p.value for p in self.rawValue],  # 提取数值数组
            'raw_timestamps': [p.timestamp for p in self.rawValue],  # 单独保存时间戳
            'frequency_data': {
                'delta': self.delta,
                'theta': self.theta,
                'low_alpha': self.lowAlpha,
                'high_alpha': self.highAlpha,
                'low_beta': self.lowBeta,
                'high_beta': self.highBeta,
                'low_gamma': self.lowGamma,
                'mid_gamma': self.midGamma
            },
            'attention': self.attention,
            'meditation': self.meditation,
            'poor_signal': self.poorSignal,
            'blink_strength': self.blinkStrength
        }


class TabletDataBuffer:
    """
    平板端数据缓冲区 - 内存优化的数据存储
    
    使用环形缓冲区和定时清理机制，支持40个平板端的高频数据传输
    而不会导致内存溢出。
    """
    
    def __init__(self, max_devices: int = 50, buffer_size_per_device: int = 300, 
                 cleanup_interval: int = 60, max_memory_mb: int = 100):
        """
        初始化数据缓冲区
        
        Args:
            max_devices: 最大支持设备数
            buffer_size_per_device: 每个设备的缓冲区大小（秒数）
            cleanup_interval: 清理间隔（秒）
            max_memory_mb: 最大内存使用量（MB）
        """
        self.max_devices = max_devices
        self.buffer_size_per_device = buffer_size_per_device
        self.cleanup_interval = cleanup_interval
        self.max_memory_mb = max_memory_mb
        
        # 使用字典存储每个设备的环形缓冲区
        self.device_buffers: Dict[str, deque] = {}
        self.device_last_update: Dict[str, datetime] = {}
        
        # 统计信息
        self.stats = {
            'total_received': 0,
            'total_dropped': 0,
            'active_sessions': 0,
            'memory_usage_mb': 0,
            'last_cleanup': datetime.now()
        }
        
        # 线程锁
        self._lock = threading.RLock()
        
        # 启动清理线程
        self._cleanup_thread = threading.Thread(target=self._cleanup_loop, daemon=True)
        self._cleanup_running = True
        self._cleanup_thread.start()
    
    def add_data(self, data: TabletBrainWaveData) -> bool:
        """
        添加数据到缓冲区
        
        Args:
            data: 平板端数据
            
        Returns:
            是否成功添加
        """
        with self._lock:
            try:
                session_id = data.session_id
                
                # 检查会话数量限制
                if session_id not in self.device_buffers and len(self.device_buffers) >= self.max_devices:
                    self.stats['total_dropped'] += 1
                    return False
                
                # 检查内存使用量
                if self._get_memory_usage() > self.max_memory_mb:
                    self._emergency_cleanup()
                    if self._get_memory_usage() > self.max_memory_mb:
                        self.stats['total_dropped'] += 1
                        return False
                
                # 初始化会话缓冲区
                if session_id not in self.device_buffers:
                    self.device_buffers[session_id] = deque(maxlen=self.buffer_size_per_device)
                    self.stats['active_sessions'] += 1
                
                # 添加数据（使用紧凑格式）
                compact_data = data.to_compact_dict()
                self.device_buffers[session_id].append(compact_data)
                self.device_last_update[session_id] = datetime.now()
                
                self.stats['total_received'] += 1
                return True
                
            except Exception as e:
                print(f"添加数据失败: {e}")
                self.stats['total_dropped'] += 1
                return False
    
    def get_latest_data(self, count: int = 1) -> List[Dict[str, Any]]:
        """
        获取最新数据
        
        Args:
            count: 获取数据条数
            
        Returns:
            最新数据列表
        """
        with self._lock:
            # 从所有缓冲区中获取最新数据
            all_data = []
            for buffer in self.device_buffers.values():
                all_data.extend(list(buffer))
            
            # 按时间戳排序并返回最新的数据
            if all_data:
                all_data.sort(key=lambda x: x.get('timestamp', 0), reverse=True)
                return all_data[:count]
            return []
    
    def get_all_data(self) -> List[Dict[str, Any]]:
        """
        获取所有数据
        
        Returns:
            所有数据列表
        """
        with self._lock:
            all_data = []
            for buffer in self.device_buffers.values():
                all_data.extend(list(buffer))
            return all_data
    
    def get_device_data(self, session_id: str) -> List[Dict[str, Any]]:
        """
        获取指定设备的数据
        
        Args:
            session_id: 会话ID
            
        Returns:
            指定设备的数据列表
        """
        with self._lock:
            if session_id in self.device_buffers:
                return list(self.device_buffers[session_id])
            return []
    
    def get_active_devices(self) -> List[str]:
        """获取活跃设备列表"""
        with self._lock:
            return list(self.device_buffers.keys())
    
    def get_stats(self) -> Dict[str, Any]:
        """获取缓冲区统计信息"""
        with self._lock:
            self.stats['memory_usage_mb'] = self._get_memory_usage()
            self.stats['active_devices'] = len(self.device_buffers)
            return self.stats.copy()
    
    def _get_memory_usage(self) -> float:
        """获取当前内存使用量（MB）- 更准确的估算"""
        try:
            # 更准确的估算：每个TabletBrainWaveData包含512个原始数据点
            # 加上8个频段数据、注意力、冥想等字段，估算每个数据包约5KB
            total_data_points = sum(len(buffer) for buffer in self.device_buffers.values())
            estimated_mb = total_data_points * 5.0 / 1024  # 5KB per data point
            return estimated_mb
        except:
            return 0.0
    
    def _cleanup_loop(self):
        """清理循环线程"""
        while self._cleanup_running:
            try:
                time.sleep(self.cleanup_interval)
                self._regular_cleanup()
            except Exception as e:
                print(f"清理线程错误: {e}")
    
    def _regular_cleanup(self):
        """定期清理过期数据"""
        with self._lock:
            current_time = datetime.now()
            expired_sessions = []
            
            # 查找过期会话（超过5分钟无数据）
            for session_id, last_update in self.device_last_update.items():
                if (current_time - last_update).total_seconds() > 300:  # 5分钟
                    expired_sessions.append(session_id)
            
            # 清理过期会话
            for session_id in expired_sessions:
                if session_id in self.device_buffers:
                    del self.device_buffers[session_id]
                if session_id in self.device_last_update:
                    del self.device_last_update[session_id]
                self.stats['active_devices'] -= 1
            
            # 强制垃圾回收
            if expired_sessions:
                gc.collect()
            
            self.stats['last_cleanup'] = current_time
            
            print(f"清理完成: 移除{len(expired_sessions)}个过期会话, "
                  f"当前活跃会话: {len(self.device_buffers)}, "
                  f"内存使用: {self._get_memory_usage():.1f}MB")
    
    def _emergency_cleanup(self):
        """紧急清理 - 内存使用过高时"""
        with self._lock:
            print("触发紧急清理...")
            
            # 清理最旧的数据
            for session_id in list(self.device_buffers.keys()):
                buffer = self.device_buffers[session_id]
                if len(buffer) > 60:  # 只保留最近60秒数据
                    # 清理一半数据
                    for _ in range(len(buffer) // 2):
                        if buffer:
                            buffer.popleft()
            
            # 强制垃圾回收
            gc.collect()
    
    def stop(self):
        """停止缓冲区"""
        self._cleanup_running = False
        if self._cleanup_thread.is_alive():
            self._cleanup_thread.join(timeout=2.0)
    
    def clear_all_data(self):
        """清空所有数据"""
        with self._lock:
            self.device_buffers.clear()
            self.device_last_update.clear()
            self.stats['total_received'] = 0
            self.stats['total_dropped'] = 0
            self.stats['active_sessions'] = 0
            logger.info("已清空所有缓冲区数据")


class TabletDataResponse(BaseModel):
    """平板端数据响应模型"""
    status: str = Field("success", description="响应状态")
    message: str = Field("数据接收成功", description="响应消息")
    session_id: str = Field(..., description="会话ID")
    timestamp: int = Field(default_factory=lambda: int(datetime.now().timestamp() * 1000), description="响应时间戳")


class TabletDataValidator:
    """平板端数据验证工具"""
    
    @staticmethod
    def validate_brain_wave_data(data: TabletBrainWaveData) -> bool:
        """验证脑波数据的合理性"""
        try:
            # 检查频段功率数据是否合理
            frequency_powers = [
                data.delta, data.theta, data.low_alpha, data.high_alpha,
                data.low_beta, data.high_beta, data.low_gamma, data.mid_gamma
            ]
            
            # 至少应该有一些频段有功率值
            if all(power == 0 for power in frequency_powers):
                return False
            
            # 检查注意力和冥想值是否在合理范围
            if not (0 <= data.attention <= 100 and 0 <= data.meditation <= 100):
                return False
            
            # 检查信号质量
            if data.poor_signal > 200:
                return False
                
            return True
            
        except Exception:
            return False
    
    @staticmethod
    def is_good_signal_quality(poor_signal: int) -> bool:
        """判断信号质量是否良好"""
        return poor_signal < 50  # 小于50认为是可用信号
    
    @staticmethod
    def get_signal_quality_description(poor_signal: int) -> str:
        """获取信号质量描述"""
        if poor_signal == 0:
            return "优秀"
        elif poor_signal <= 25:
            return "良好"
        elif poor_signal <= 50:
            return "一般"
        elif poor_signal <= 100:
            return "较差"
        else:
            return "无信号"


class TabletDataConverter:
    """平板端数据转换工具"""
    
    @staticmethod
    def to_internal_format(tablet_data: TabletBrainWaveData) -> dict:
        """
        将平板端数据转换为内部BrainWaveData格式
        
        Args:
            tablet_data: 平板端数据
            
        Returns:
            内部格式的数据字典
        """
        from ..models.brain_wave_data import FrequencyData
        
        # 创建频域数据
        frequency_data = FrequencyData(
            # 8个频段功率数据（转换为float）
            delta=float(tablet_data.delta),
            theta=float(tablet_data.theta),
            low_alpha=float(tablet_data.low_alpha),
            high_alpha=float(tablet_data.high_alpha),
            low_beta=float(tablet_data.low_beta),
            high_beta=float(tablet_data.high_beta),
            low_gamma=float(tablet_data.low_gamma),
            mid_gamma=float(tablet_data.mid_gamma),
            
            # NeuroSky算法输出
            attention=float(tablet_data.attention),
            meditation=float(tablet_data.meditation),
            
            # 信号质量和其他指标
            poor_signal=tablet_data.poor_signal,
            blink_strength=tablet_data.blink_strength,
            
            # 原始数据 - 提取RawValuePoint对象的value值
            raw_value=[point.value if hasattr(point, 'value') else point for point in tablet_data.raw_value] if isinstance(tablet_data.raw_value, list) else tablet_data.raw_value,
            
            # 时间戳
            timestamp=datetime.fromtimestamp(tablet_data.timestamp / 1000),
            is_filtered=False  # 平板端发送的是原始数据
        )
        
        return {
            'session_id': tablet_data.session_id,
            'room_id': tablet_data.room_id,
            'timestamp': datetime.fromtimestamp(tablet_data.timestamp / 1000),
            'frequency_data': frequency_data,
            'raw_value': [point.value if hasattr(point, 'value') else point for point in tablet_data.raw_value] if isinstance(tablet_data.raw_value, list) else tablet_data.raw_value,
            'signal_quality': tablet_data.poor_signal
        }
    
    @staticmethod
    def from_compact_dict(compact_data: Dict[str, Any]) -> TabletBrainWaveData:
        """
        从紧凑字典格式恢复为完整数据模型
        
        Args:
            compact_data: 紧凑格式数据
            
        Returns:
            完整的TabletBrainWaveData对象
        """
        return TabletBrainWaveData(
            session_id=compact_data['s'],
            room_id=compact_data['room'],
            timestamp=compact_data['t'],
            raw_value=compact_data['r'],
            delta=compact_data['f'][0],
            theta=compact_data['f'][1],
            low_alpha=compact_data['f'][2],
            high_alpha=compact_data['f'][3],
            low_beta=compact_data['f'][4],
            high_beta=compact_data['f'][5],
            low_gamma=compact_data['f'][6],
            mid_gamma=compact_data['f'][7],
            attention=compact_data['a'],
            meditation=compact_data['m'],
            poor_signal=compact_data['p'],
            blink_strength=compact_data['b']
        )


class TabletDataConstants:
    """平板端数据常量"""
    
    # 数据上传频率
    UPLOAD_INTERVAL = 1  # 秒，每秒上传一次
    
    # API端点
    DATA_ENDPOINT = "/api/tablet/brainwave"
    
    # 数据范围常量（对应NeuroPy3的范围）
    RAW_VALUE_MIN = -32768
    RAW_VALUE_MAX = 32767
    FREQUENCY_POWER_MAX = 16777215  # 24位最大值
    ATTENTION_MAX = 100
    MEDITATION_MAX = 100
    POOR_SIGNAL_MAX = 200
    BLINK_STRENGTH_MAX = 255
    
    # 信号质量阈值
    SIGNAL_EXCELLENT = 0
    SIGNAL_GOOD = 25
    SIGNAL_FAIR = 50
    SIGNAL_POOR = 100
    SIGNAL_NO_CONTACT = 200
    
    # 内存管理常量
    MAX_DEVICES = 50                    # 最大支持设备数
    BUFFER_SIZE_PER_DEVICE = 300       # 每设备缓冲区大小（秒）
    CLEANUP_INTERVAL = 60              # 清理间隔（秒）
    MAX_MEMORY_MB = 500                # 最大内存使用（MB）- 增加到500MB
    DEVICE_TIMEOUT = 300               # 设备超时时间（秒）


# 全局数据缓冲区实例
_global_buffer = None

def get_global_buffer() -> TabletDataBuffer:
    """获取全局数据缓冲区实例"""
    global _global_buffer
    if _global_buffer is None:
        _global_buffer = TabletDataBuffer(
            max_devices=TabletDataConstants.MAX_DEVICES,
            buffer_size_per_device=TabletDataConstants.BUFFER_SIZE_PER_DEVICE,
            cleanup_interval=TabletDataConstants.CLEANUP_INTERVAL,
            max_memory_mb=TabletDataConstants.MAX_MEMORY_MB
        )
    return _global_buffer